import os

#from django.conf.global_settings import MEDIA_ROOT
from django.contrib import messages
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.utils import timezone

from django_study.settings import MEDIA_ROOT
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.db.models.functions import datetime
from django.http import HttpResponse
from django.shortcuts import redirect, render

# Create your views here.
# 测试 1
# Django 会把用户的请求信息封装成 request 对象，并传递给视图函数。
from django.utils.decorators import method_decorator
from django.views import View

import user
from demo.form import ArticleForms
from demo.models import Classes, UserDetail, Article, Label


def test(request, *args, **kwargs):
    # info = request.GET.get('info') 保存了 URL 中传递的参数字符串
    print(kwargs)
    info = kwargs.get('in')
    print(info)
    # 返回一个响应对象 返回给 用户
    return HttpResponse("<h1 style='color:red;'>你好！ Django<h1/>")


# 测试 2
def TV(request, *args, **kwargs):
    return HttpResponse("<h1 style='color:red;'>电视机</h1>")


# #
# #
# # 测试 3
# # 点击不同的选项，显示不同的内容
# # 报文 请求方式
def nav(request, **kwargs):
    # 获取路由传递的参数 得到地址栏传递过来的分类
    # 保存的是什么 转换器参数接收到字符数据
    classes = kwargs.get('classes')
    username = request.user.username
    # 得到模型类里面的分类对象 得到所有文章
    try:
        res = Classes.objects.get(name=classes)
    except Classes.DoesNotExist:
        # 返回一个响应对象 如果分类对象不存在跳转到错误页面
        return redirect('not_find')

    return HttpResponse(render(request, 'demo/nav.html', context={
        # 通过管理器获取分类下所有文章数据
        'articles': res.article_set.all(),
        # 当前路由访问的分类名 一个分类名对应一个文章列表
        'now_cls': classes,
        # 得到模型类里面的所有分类对象
        'classes': Classes.objects.all(),
        'user': username,
    }))


# # 跳转首页函数
def index(request, **kwargs):
    # Django会自动获取请求中的cookie 然后到数据库中的session匹配
    username = request.user.username
    return HttpResponse(render(request, 'demo/index.html',
                               context={
                                   'classes': Classes.objects.all(),  # 数据当中所有分类名称
                                   'user': username,  # 用户信息
                               }))


# 重定向处理
def redirect_demo(request, **kwargs):
    return HttpResponse("页面不存在，小主！请勿非法访问！")


# 数据的增删改查
def classes(request, **kwargs):
    # 1-实例化对象 并且传入字段值
    # cls1 = Classes(name="Django", id=4)
    # cls1.save()   # 保存数据

    # 2-创建对象
    # cls2 = Classes()
    # # 给模型对象属性赋值
    # cls2.name = "java"
    # cls2.save()  # 保存数据
    #
    # # 3-通过类传值的方式创建对象传值
    # cls3 = Classes.objects.create(name="deepseek")
    #
    # # 4-通过 get_or_create 方法有数据则返回数据 没有则创建数据
    # cls4 = Classes.objects.get_or_create(name="html", id=4)

    # 查询所有数据
    # res = Classes.objects.all()
    # for i in res:
    #     print(i.name)
    # print(res)   # 返回 <QuerySet [<Classes: python>, <Classes: java>, <Classes: deepseek>, <Classes: html>]>
    # 查询单条数据
    # res1 = Classes.objects.get(id=1)
    # res1 = Classes.objects.get(name="python")
    # print(res1)
    # 查询多条数据 或索引取值
    # res2 = Classes.objects.filter(name="java")[1]
    # print(res2)

    # 数据更新
    # 修改单条数
    # 先获取你要修改的数据
    # res1 = Classes.objects.get(name="html")
    # res1.name = "HTML"
    # res1.save()  # 修改后保存数据

    # 修改多条数据 查询集不是一个对象
    # res2 = Classes.objects.filter(name="python").update(name="Python3")

    # 数据删除
    # 删除单条数据
    # res1 = Classes.objects.get(name="html").delete()
    # 删除多条数据
    # res2 = Classes.objects.filter(name="python3").delete()

    # 数据查询方法
    # 获取第一条数据
    # print(Classes.objects.first())
    # 获取最后一条数据
    # print(Classes.objects.last())
    # 排除查询
    # print(Classes.objects.exclude(name="html"))
    # 对结果进行排序
    # print(Classes.objects.order_by("id"))
    # 对结果进行逆向排序
    # print(Classes.objects.order_by("-id"))
    # 获取指定字段
    # print(Classes.objects.values("name"))
    # 获取指定字段并去重
    # print(Classes.objects.values("name").distinct())

    # # 查询条件
    # # 根据字符值进行查询
    # # 忽略大小写
    # print(Classes.objects.filter(name__icontains="HTML"))
    # # 包含 你指定的字符
    # print(Classes.objects.filter(name__contains="t"))
    # # 以什么字符开始
    # print(Classes.objects.filter(name__startswith="d"))
    # # 以什么字符结束
    # print(Classes.objects.filter(name__endswith="k"))
    # # 成员 字段值是否在你给定的范围里面
    # print(Classes.objects.filter(id__range=(1, 3)))
    # print(Classes.objects.filter(name__in=["python", "java", "html"]))

    # # 根据数字值进行比较
    # # 大于
    # print(Classes.objects.filter(id__gt=1))
    # # 大于等于
    # print(Classes.objects.filter(id__gte=2))
    # # 小于
    # print(Classes.objects.filter(id__lt=3))
    # # 小于等于
    # print(Classes.objects.filter(id__lte=4))
    # # 区间 范围
    # print(Classes.objects.filter(id__range=(1, 3)))
    # # 判断字段是否为空
    # print(Classes.objects.filter(name__isnull=True))
    # return HttpResponse("""数据更新成功""")
    return HttpResponse("""数据更新成功""")


def tab_info(request, **kwargs):
    # 一对一表数据添加
    # 创建一个 用户对象
    # user = User.objects.create_user(username="rose")
    # # 用户密码加密
    # user.set_password("qwe123")
    # # 保存用户数据
    # user.save()
    # 创建一个用户信息对象
    # 方法一：如果上面有用户对象，则关联用户对象
    # UserDetail.objects.create(user=user, age=18, sex="女")
    # # 方法二：如果没有对应的关联对象，可以通过用户ID指定关联的用户对象
    # UserDetail.objects.create(user_id=2, age=19, sex="女")

    # 一对一表数据查询
    # 获取用户为 Lisa 的年龄
    # 先获取用户对象
    # user = User.objects.get(username="Lisa")
    # 因为用户表关联了用户详情表 所有用户对象拥有一个用户详情表属性 相当于得到了用户详情对象
    # 可以直接通过 .字段名 获取对应的属性值
    # print(user.userdetail.age)

    # 获取ID为 1 的用户详情对象的名字
    # 先获取用户详情对象 .字段名 获取对应的属性值
    # print(UserDetail.objects.get(id=1).user.username)

    # 一对多表数据添加
    # 创建一个分类表对象
    # classes = Classes.objects.create(name='人工智能')
    # # 创建一个文章对象
    # article = Article(title='Python 包管理工具核心指令uv sync解析',
    #                   desc="Python 包管理工具核心指令uv sync解析",
    #                   content="`uv sync` 是 **uv** 工具的核心命令之一，用于安装项目依赖并生成/更新 `uv.lock` 文件。以下是关于依赖定义来源和锁文件作用的详细解析：",
    #                   cover="https://i-blog.csdnimg.cn/direct/ffb470100e2a435483f600003e283bbd.png",
    #                   user_id=1,
    #                   classes_id=15)  # 关联分类表对象
    # article.save()  # 保存数据

    # 一对多表数据查询
    # 使用管理器查询test分类下的所有文章
    # print(Classes.objects.get(name="test").article_set.all())
    # 使用管理器查询test分类下的第一个文章
    # print(Classes.objects.get(name="test").article_set.get(title="test").content)

    # 一对多管理器表数据添加
    # classes = Classes.objects.get(name="test")
    # 通过管理器来添加文章
    # classes.article_set.create(title="test_three",
    #                            desc="NumPy数组 和 原生Python Array（数组）之间有几个重要的区别：",
    #                            content="NumPy 数组元素需具有相同的数据类型，因此在内存中的大小相同",
    #                            cover="https://i-blog.csdnimg.cn/direct/e57771da0f12498cb733a68e6dfff5ef.png",
    #                            user_id=1,
    #                            classes=classes,
    #                            )

    # 一对多管理器表修改
    # 通过管理器将 test分类下标题为 test_three 的文章修改添加到 NumPy 分类下
    # 就在 NumPy 分类下添加了标题为 test_three 的文章
    # classes = Classes.objects.get(name="python")
    # classes.article_set.add(Article.objects.get(title="numpy_one"))

    # 多对多的表数据添加
    # 给 numpy_one 文章添加两个标签
    # 先一步获取numpy_one 文章对象
    # article = Article.objects.get(title="numpy_one")
    # # article.label.add(Label.objects.get(name="lisa"))
    # article.label.create(name="人工智能")
    # article.label.create(name="华为云")
    # article.label.create(name="Dify-LLM")
    # article.label.create(name="华为云Flexus")

    # 表数据删除
    # 删除的是关系表中的记录 不会删除标签表的标签数据
    # 第一步先获取 numpy_one 删除的标签对象
    # article = Article.objects.get(title="numpy_one")
    # 第二步获取删除 numpy_one_two 的标签对象
    # article.label.remove(Label.objects.get(name="numpy_one_two"))
    # 删除全部数据 删除关系表中的记录
    # article.label.clear()

    # 多表查询
    # 获取 numpy_one 的标签对象
    # article = Article.objects.get(title="numpy_one")
    # print(article.label.filter(name="numpy_one_two"))
    # 获取 numpy_one 的所有标签对象
    # print(article.label.all())
    # 获取 numpy_one 的文章对象
    # labels = Label.objects.filter(article__title="numpy_one")
    # for label in labels:
    #     print(label.name)

    # 管理器获取关联对象
    # label = Label.objects.get(name="numpy_one_one")
    # print(label.article_set.all())

    # 获取 ID 为 1 的分类的文章的用户详情
    # print(UserDetail.objects.filter(user__article__classes_id=7))
    # 获取 test 分类下的所有文章
    # print(Article.objects.filter(classes__name="test"))

    return HttpResponse("""数据更新成功""")


# 发布页面
class AddArticleView(View):
    # 添加登录校验的权限 用的 auth 系统 不会在 session 表中查询登录用户了
    @method_decorator(login_required)
    def get(self, request):
        form = ArticleForms()
        username = request.user.username
        return render(request, "demo/add_article.html", {"user": username, "form": form})

    def post(self, request):
        # 检查是否是继续发布操作
        if 'continue' in request.POST:
            # 继续发布，保留表单数据或清空表单
            form = ArticleForms()
            username = request.user.username
            if form.is_valid():
                # 添加成功提示信息
                messages.success(request, '文章发布成功！')
                return render(request, "demo/add_article.html", {
                    "user": username,
                    "form": form,
                    "success": True  # 标识发布成功
                })

        # 正常发布流程
        # 绑定用户数据表单对象
        # 当需要使用表单时并且需要绑定文件数据 文件数据需要跟普通的表单数据分开处理
        # 文件数据并不会放到 POST 中 而是需要使用 request.FILES
        form = ArticleForms(request.POST, request.FILES)
        username = request.user.username

        if form.is_valid():
            # 注意：这里去掉行末的逗号，避免创建元组
            title = form.cleaned_data['title']
            desc = form.cleaned_data['desc']
            content = form.cleaned_data['content']
            cover = form.cleaned_data['cover']  # 从表单数据中获取cover
            classes = form.cleaned_data['classes']
            labels = form.cleaned_data['label']

            # 保存图片时 图片名可能会出现重复 为了图片的名字不重复 可以使用时间戳 把时间戳跟图片名拼接起来
            file_name = f'{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}_{cover.name}'
            # 图片保存到服务器中
            with open(os.path.join(MEDIA_ROOT, file_name), 'wb') as fp:
                # 当图片文件过大的时候 可以使用 chunks() 方法来分块保存
                for data in cover.chunks():
                    fp.write(data)

            # 创建文章模型对象
            article = Article.objects.create(
                title=title,
                desc=desc,
                content=content,
                cover=file_name,
                classes=classes,
                user=request.user  # 添加用户信息
            )

            # 添加标签 多对多已有数据的绑定
            for i in labels:
                article.label.add(i)

            # 保存多对多关系
            article.save()

            # 发布成功后显示选择页面
            return render(request, "demo/add_article.html", {
                "user": username,
                "form": ArticleForms(),  # 清空表单
                "success": True,  # 标识发布成功
                "article_title": title  # 传递文章标题用于显示
            })

        # 表单验证失败时也要返回表单（包含错误信息）
        return render(request, "demo/add_article.html", {"user": username, "form": form})


# 博客详情页
class ArticleDetailView(View):
    # 查看一篇文章的详情内容 需要通过 article_id 来获取文章的详情
    def get(self, request, article_id):
        try:
            article = Article.objects.get(id=article_id)
            cover = article.cover
        except Article.DoesNotExist:
            return redirect("not_find")
        return render(request, "demo/article_detail.html", {
            'article': article,
            'cover': cover,
            'labels': article.label.all(),
        })


# 创造中心页面 跟文章添加页面一样需要先登录才能查看
class ArticleUserView(View):
    # 登录校验
    @method_decorator(login_required)
    def get(self, request):
        # 获取当前登录用户的所有文章
        articles = Article.objects.filter(user=request.user)
        # 文章数量庞大 需做分页处理 Paginator方法自动做分页的操作 默认每页显示10条数据
        paginator = Paginator(articles, 5)
        try:
            # 获取当前页码
            page = request.GET.get('page')
            # 获取当前页码的数据(对象)
            pages = paginator.page(page)
        except PageNotAnInteger:
            # 如果当前页码不是数字 默认显示第一页
            pages = paginator.page(1)
        except EmptyPage:
            # 如果当前页码超出范围 显示最后一页
            pages = paginator.page(paginator.num_pages)
        # 返回用户文章列表页面
        return render(request, "demo/user_article.html", context={
            'user': request.user.username,
            'pages': pages,  # 只传递分页后的数据
        })


# 修改文章页面
class ArticleUpdateView(View):
    # 登录校验
    @method_decorator(login_required)
    def get(self, request, article_id):
        try:
            article = Article.objects.get(id=article_id)
        except Article.DoesNotExist:
            return redirect('not_find')
        else:
            # 获取所有分类
            classes = Classes.objects.all()
            # 获取文章当前的标签
            labels = article.label.all()
            # 如果能够正常获取到文章对象数据 就定义一个字典存储修改之前的文章数据
            article_data = {
                'title': article.title,
                'desc': article.desc,
                'content': article.content,
                'cover': article.cover,
                'classes': article.classes,
                'label': article.label.all(),
            }
            form = ArticleForms(article_data)
        return render(request, 'demo/update_article.html', context={
            'article': article,
            'form': form,
            'user': request.user.username,
            'classes': classes,  # 传递所有分类
            'labels': Label.objects.all(),  # 传递所有标签
        })

    @method_decorator(login_required)
    def post(self, request, article_id):
        form = ArticleForms(request.POST, request.FILES)
        username = request.user.username

        if form.is_valid():
            title = form.cleaned_data['title']
            desc = form.cleaned_data['desc']
            content = form.cleaned_data['content']
            cover = form.cleaned_data['cover']  # 从表单数据中获取cover
            classes = form.cleaned_data['classes']
            labels = form.cleaned_data['label']

            try:
                article = Article.objects.get(id=article_id)
                old_cover_path = os.path.join(MEDIA_ROOT, str(article.cover))  # 确保 cover 是字符串类型

                # 如果修改文章时选择了新的封面图片，则删除旧的封面图片
                if cover and os.path.exists(old_cover_path):
                    os.remove(old_cover_path)

                # 生成唯一文件名
                if cover:
                    file_name = f'{timezone.now().strftime("%Y%m%d%H%M%S")}_{cover.name}'
                    with open(os.path.join(MEDIA_ROOT, file_name), 'wb') as fp:
                        for data in cover.chunks():
                            fp.write(data)
                    article.cover = file_name  # 更新 article.cover 字段
                else:
                    article.cover = article.cover  # 如果没有新封面，保留原来的封面

                # 更新文章信息
                article.title = title
                article.desc = desc
                article.content = content
                article.classes = classes
                article.save()

                # 清空并重新添加标签
                article.label.clear()
                for label in labels:
                    article.label.add(label)

            except Article.DoesNotExist:
                return redirect('not_find')

        return redirect('ArticleUser')


# 删除文章
class ArticleDeleteView(View):
    def get(self, request, article_id):
        try:
            article = Article.objects.get(id=article_id)
        except Article.DoesNotExist:
            return redirect('not_find')

        # 删除文章封面图片
        cover_path = os.path.join(MEDIA_ROOT, str(article.cover))  # 确保 cover 是字符串类型
        if os.path.exists(cover_path):
            os.remove(cover_path)

        # 删除数据库里面的文章数据
        article.delete()
        return redirect('ArticleUser')

